﻿
#pragma once

#include <utility>

namespace tools{
    namespace sfinae{

        struct Failed { enum { eIS_AVAILABLE = 0 }; };

        struct YesNo  { typedef char (&No)[1]; typedef char (&Yes)[2]; };

        template<class T, class U> struct Same { typedef YesNo::No  Result; };
        template<class T> struct Same<T,T>     { typedef YesNo::Yes Result; };

    //------------------------------------------------------
        template<class C, class T> struct Transform;
        
        template<class C, class R> struct Transform<C,R()>
        {
            typedef R Ret;
            typedef R(*Func);
            typedef R(C::*Method)();
            typedef R(C::*CMethod)()const;
        };
        template<class C, class R,class A1> struct Transform<C,R(A1)> 
        {
            typedef R Ret;
            typedef R(*Func)(A1);
            typedef R(C::*Method)(A1);
            typedef R(C::*CMethod)(A1)const;
        };
        template<class C, class R,class A1,class A2> struct Transform<C,R(A1,A2)>  
        {
            typedef R Ret;
            typedef R(*Func)(A1,A2);
            typedef R(C::*Method)(A1,A2);
            typedef R(C::*CMethod)(A1,A2)const;
        };
        template<class C, class R,class A1,class A2,class A3> struct Transform<C,R(A1,A2,A3)>
        {
            typedef R Ret;
            typedef R(*Func)(A1,A2,A3);
            typedef R(C::*Method)(A1,A2,A3);
            typedef R(C::*CMethod)(A1,A2,A3)const;
        };
        
    //------------------------------------------------------

        template<class C, class T> struct Base
        {
            typedef T C::*Method;
            typedef T C::*CMethod;
        };

        template<class C, class R> struct Base<C, R()>
        {
            typedef R(C::*Method)();
            typedef R(C::*CMethod)()const;

        };
        template<class C, class R,class A1> struct Base<C, R(A1)>
        {
            typedef R(C::*Method)(A1);
            typedef R(C::*CMethod)(A1)const;
        };
        template<class C, class R,class A1,class A2> struct Base<C, R(A1,A2)>
        {
            typedef R(C::*Method)(A1,A2);
            typedef R(C::*CMethod)(A1,A2)const;
        };
        template<class C, class R,class A1,class A2,class A3> struct Base<C, R(A1,A2,A3)>
        {
            typedef R(C::*Method)(A1,A2,A3);
            typedef R(C::*CMethod)(A1,A2,A3)const;
        };

        template<class C, class T> struct SFINAE: Base<C, T>,  YesNo
        {
            typedef typename Base<C, T>::Method  Method;
            typedef typename Base<C, T>::CMethod CMethod;
            template<Method,  Method f > struct Helper;
            template<CMethod, Method f > struct CHelper;
        };

    }//namespace sfinae
}//namespace tools

